package ch89;

import java.math.BigInteger;
import java.util.Random;
import java.io.*;
public class ElGamal {
    /**
     * 加密过程，并返回加密结果为一个BigInteger数组
     * @param m
     *             明文
     * @param p
     * @param b
     *             公钥
     * @param g
     * @return 返回一个大整数型的加密结果
     */
    public static BigInteger[] encrypt(BigInteger m, BigInteger p,BigInteger b, BigInteger g) {
        BigInteger[] rtn = { null, null };
        BigInteger k = ElGamal.getRandomk(p);

        BigInteger C1 = g.modPow(k, p);
        BigInteger C2 = m.multiply(b.modPow(k, p)).mod(p);
        rtn[0] = C1;
        rtn[1] = C2;
        return rtn;
    }
    /**
     * 解密过程，返回值为一个BigInteger对象
     *
     * @param C1
     * @param C2
     * @param a
     * @param p
     * @return 返回一个大整数型的解密结果
     */
    public static BigInteger decrypt(BigInteger C1, BigInteger C2,
                                     BigInteger a, BigInteger p) {
        BigInteger m = null;
        m = C2.multiply(C1.modPow(a.negate(), p)).mod(p);
        return m;
    }
    /**
     * 加密一个数字签名,其实就是加密一个字符串,消息m为一个字串,程序中是把消息m(String)转成一个byte数组,
     * 然后再把byte类型的数组再构造成一个BigInteger对象,使用encrypt方法加密
     *
     * @param m
     * @param p
     * @param b
     * @param g
     * @return
     */
	/*public static BigInteger[] encryptSignature(String m, BigInteger p,
			BigInteger b, BigInteger g) {
		BigInteger[] rtn = { null, null };// 定义一个BigInteger数组,存贮返回的加密对象C1,C2
		BigInteger message = new BigInteger(m.getBytes());// 把字串转成一个BigInteger对象
		rtn = ElGamal.encrypt(message, p, b, g);// 调用加密方法encrypt加密
		return rtn;
	}*/

    /**
     * 解密一个数字签名,传入先前加密结果,两个BigInteger的对象C1,C2
     * 程序使用先前的decrypt方法进行解密,把解密得到的结果(BigInteger对象),转化为一个byte的数组
     * 再把byte类型的数组还原为一个字串
     *
     * @param C1
     * @param C2
     * @param a
     * @param p
     * @return
     */
	/*public static String decryptSignature(BigInteger C1, BigInteger C2,
			BigInteger a, BigInteger p) {
		BigInteger rtn = ElGamal.decrypt(C1, C2, a, p);// 调用decrypt方法解密
		String str = new String(rtn.toByteArray());// 把返回的结果还原成一个字串
		return str;
	}*/

    /**
     * 取随机数k
     *
     * @param p
     * @return
     */
    private static BigInteger getRandomk(BigInteger p) {
        // /随机取一个与p-1互质的数k & 0<=k<p-1
        Random r = new Random();
        BigInteger k = null;
        while (true) {
            k = new BigInteger(p.bitLength() - 1, r);// 产生一0<=k<p-1的随机数
            if (k.gcd(p.subtract(BigInteger.ONE)).equals(BigInteger.ONE)) {// 如果随机数与p-1互质
                // 则选取成功,返回随机数k
                break;
            }
        }
        return k;
    }

    /**
     * 取一个大的随机素数P,和P的生成元a
     *
     * @param alpha
     *            为该素数的比特位数
     * @return 返回大整数型数组
     */
    public static BigInteger[] getRandomP(int alpha) {
        BigInteger rtn[] = { null, null };
        Random r = new Random();
        BigInteger p = null;
        BigInteger q = null;
        while (true) {
            q = BigInteger.probablePrime(alpha, r);
            if (q.bitLength() != alpha)
                continue;
            if (q.isProbablePrime(10))
            {
                p = q.multiply(new BigInteger("2")).add(BigInteger.ONE);
                if (p.isProbablePrime(10))
                    break;
            }
        }
        BigInteger g = null;
        while (true) {
            g = BigInteger.probablePrime(p.bitLength() - 1, r);
            if (!g.modPow(BigInteger.ONE, p).equals(BigInteger.ONE)
                    && !g.modPow(q, p).equals(BigInteger.ONE)) {
                break;
            }
        }
        rtn[0] = p;
        rtn[1] = g;
        return rtn;
    }

    /**
     * 取随机数a
     *
     * @param p
     * @return 返回大整数型的随机数a
     */
    public static BigInteger getRandoma(BigInteger p) {
        BigInteger a = null;
        Random r = new Random();
        a = new BigInteger(p.bitLength() - 1, r);
        return a;
    }

    /**
     * 计算b的值
     *
     * @param g
     * @param a
     * @param p
     * @return 返回大整数型的b值
     */
    public static BigInteger calculateb(BigInteger g, BigInteger a, BigInteger p) {
        BigInteger b = null;
        b = g.modPow(a, p);
        // ///b=g^a(mod p)
        return b;
    }
}
